/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

import {
  BaseCommand,
  FeatureManager,
  pickedFaceData,
  pickedLineData,
} from '@amcax/base';
import {
  NumberInputFormat,
  PickInputRJSFFormat,
} from '@app-cad/common/components/command-form/constants';
import { PickableCommandForm } from '@app-cad/common/components/command-form/pickable';
import { composeFormat } from '@app-cad/common/components/command-form/utils';
import { useCommand } from '@app-cad/common/hooks/useCommand';
import { useTools } from '@app-cad/common/hooks/useTools';
import { getIdOfPickedObj } from '@app-cad/common/utils/render';
import { ctx2 } from '@app-cad/module';
import { FeaturedCmdDialogParams } from '@app-cad/types';
import { useAsyncEffect } from 'ahooks';
import i18n from 'i18next';
import { useMemo, useRef, useState } from 'react';
import { generateProductPickObj } from '../../../../common/components/pick-input/pick-product';
import { pickedProductData } from '../../../../common/components/pick-input/types';
import { get1stLevelPartFromId } from '../../utils';
import { useDollyCamera } from '../common/hooks/useDollyCamera';
import {
  clearArrayCommandPreview,
  executeArrayCommand,
  onArrayCommandExit,
} from '../common/utils/ArrayUtils';

enum DisMode {
  DISTANCE = 'distance',
  ANGLE = 'angle',
}

type FormData = {
  alignSource: boolean;
  disMode: DisMode;
  base: pickedProductData;
  angle: number;
  distance: number;
  count: number;
  axis: pickedLineData | pickedFaceData;
  reverse: boolean;
  skipInstances: boolean;
};

function CircularArray({
  title,
  onClose,
  placement,
  props,
}: FeaturedCmdDialogParams) {
  useDollyCamera();

  const [formData, setFormData] = useState<FormData>({
    alignSource: false,
    disMode: DisMode.DISTANCE,
    base: null,
    angle: 360,
    distance: 90,
    count: 4,
    axis: null,
    reverse: false,
    skipInstances: false,
  });
  const { pickerAssembly } = useTools();

  const skippedIds = useRef<number[]>([]);

  useAsyncEffect(async () => {
    if (props == null) {
      setFormData({
        disMode: DisMode.DISTANCE,
        base: null,
        angle: 360,
        distance: 90,
        count: 4,
        axis: null,
        reverse: false,
        alignSource: false,
        skipInstances: false,
      });
      return;
    }

    const {
      base,
      angle = 360,
      distance = 90,
      count,
      axis,
      reverse = false,
      alignSource,
      skipids = [],
    } = props.params;

    skippedIds.current = skipids;

    const baseObj = generateProductPickObj(get1stLevelPartFromId(base));
    const axisObj =
      axis &&
      (pickerAssembly.generatePickObjByPickID(axis) as
        | pickedLineData
        | pickedFaceData);

    await initEditDependency([base, axis]);

    const disMode = angle ? DisMode.ANGLE : DisMode.DISTANCE;

    setFormData({
      disMode,
      base: baseObj,
      angle,
      distance,
      count,
      axis: axisObj,
      reverse,
      alignSource,
      skipInstances: skipids.length > 0,
    });
  }, [props]);

  useAsyncEffect(async () => {
    clearArrayCommandPreview();

    if (!isFormDataValid()) {
      ctx2.viewer.requestRender();
      return;
    }
    await execute(true);
  }, [formData]);

  const isFormDataValid = () => {
    const { disMode, base, angle, distance, count, axis } = formData;

    if (disMode === DisMode.ANGLE) {
      return base != null && axis != null && count != null && angle != null;
    } else if (disMode === DisMode.DISTANCE) {
      return base != null && axis != null && count != null && distance != null;
    } else {
      return false;
    }
  };

  const finishValid = useMemo(isFormDataValid, [formData]);

  const execute = async (preview: boolean = false) => {
    const {
      disMode,
      base,
      angle,
      distance,
      count,
      axis,
      reverse,
      alignSource,
      skipInstances,
    } = formData;
    const baseID = getIdOfPickedObj(base);

    const validParams = {
      ...{
        mode: 'circle',
        alignSource,
        axis: getIdOfPickedObj(axis),
        count,
        reverse,
      },
      ...(disMode === DisMode.ANGLE
        ? { angle: angle }
        : { distance: distance }),
    };

    await executeArrayCommand(
      props,
      baseID,
      validParams,
      preview,
      base,
      skipInstances,
      skippedIds.current,
    );
  };

  const checkAndFinish = async () => {
    if (!isFormDataValid()) {
      return;
    }

    await execute();
    onExit();
  };

  const onExit = () => {
    onArrayCommandExit();
    onClose();
  };

  // =================
  const { initEditDependency } = useCommand('array-circle', {
    onClose: onExit,
    finishDraw: checkAndFinish,
  });

  const schema = useMemo(() => {
    const disModeSchema = {
      type: 'string',
      title: '模式',
      default: DisMode.DISTANCE,
      oneOf: [
        {
          type: 'string',
          title: '间距',
          const: DisMode.DISTANCE,
        },
        {
          type: 'string',
          title: '总角度',
          const: DisMode.ANGLE,
        },
      ],
    };

    const skipInstancesSchema = {
      type: 'boolean',
      title: '跳过实例',
      default: false,
    };

    if (formData.disMode === DisMode.DISTANCE) {
      return {
        properties: {
          disMode: disModeSchema,
          base: {
            type: 'string',
            format: PickInputRJSFFormat.PickProduct,
          },
          axis: {
            type: 'string',
            format: composeFormat([
              PickInputRJSFFormat.PickLine,
              PickInputRJSFFormat.PickSurface,
            ]),
          },
          count: {
            type: 'number',
            title: '阵列数量',
            default: 4,
            format: NumberInputFormat.Integer,
            minimum: 2,
          },
          distance: {
            type: 'number',
            title: '相邻角度间距',
            default: 90,
            format: NumberInputFormat.Angle,
          },
          alignSource: {
            type: 'boolean',
            title: '源对齐',
            default: false,
          },
          skipInstances: skipInstancesSchema,
        },
      };
    }

    if (formData.disMode === DisMode.ANGLE) {
      return {
        properties: {
          disMode: disModeSchema,
          base: {
            type: 'string',
            format: PickInputRJSFFormat.PickProduct,
          },
          axis: {
            type: 'string',
            format: composeFormat([
              PickInputRJSFFormat.PickLine,
              PickInputRJSFFormat.PickSurface,
            ]),
          },
          count: {
            type: 'number',
            title: '阵列数量',
            default: 4,
            format: NumberInputFormat.Integer,
            minimum: 2,
          },
          angle: {
            type: 'number',
            title: '阵列总角度',
            default: 360,
            format: NumberInputFormat.Angle,
          },
          alignSource: {
            type: 'boolean',
            title: '源对齐',
            default: false,
          },
          skipInstances: skipInstancesSchema,
        },
      };
    }

    return schema;
  }, [formData.disMode]);

  if (!FeatureManager.isFeatureEnabled('product-array-skip-instances')) {
    delete schema.properties.skipInstances;
  }

  const onReverseChange = (reverse: boolean) =>
    setFormData((prevFormData) => ({
      ...prevFormData,
      reverse,
    }));

  return (
    <PickableCommandForm
      isFormDataValid={finishValid}
      disabled={false}
      onClose={onExit}
      title={title}
      placement={placement}
      schema={schema}
      formData={formData}
      onChange={(v) => {
        setFormData(v);
      }}
      extraUiSchema={{
        base: {
          'ui:placeholder': '请输入目标模型',
          'ui:autofocus': true,
          'ui:disabled': !!props,
        },
        axis: {
          'ui:placeholder': '请输入阵列轴',
        },
        disMode: {
          'ui:widget': 'radio',
          'ui:classNames': 'command-form__mode-switch',
        },
        count: {
          'ui:options': {
            disableParameter: true,
          },
        },
        distance: {
          'ui:options': {
            showReverse: true,
            disableParameter: true,
            onReverseChange,
          },
        },
        angle: {
          'ui:options': {
            showReverse: true,
            disableParameter: true,
            onReverseChange,
          },
        },
      }}
      submitText='完成'
      onSubmit={checkAndFinish}
    />
  );
}

export const cmdCircularArray: BaseCommand = {
  id: 'cmdCircularArray',
  label: 'command.product-array-circle',
  icon: 'commands/feature/array-circle',
  create: (label, onClose, props) => (
    <CircularArray
      title={i18n.t(label)}
      onClose={onClose}
      props={props}
    />
  ),
};
